package com.nd.flink.utils;

import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authentication.util.KerberosName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.security.auth.callback.*;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class KerberosUtil {
    private static final Logger log = LoggerFactory.getLogger(KerberosUtil.class);
    public static final String DEFAULT_LOGIN_CONTEXT_NAME = "Client";

    public static void setPassJaasConf(String loginContextName, String principal, String passWord)
            throws IOException {

        javax.security.auth.login.Configuration.setConfiguration(
                new DynamicJaasConfiguration(loginContextName, principal, passWord));

        javax.security.auth.login.Configuration conf =
                javax.security.auth.login.Configuration.getConfiguration();
        if (!(conf instanceof DynamicJaasConfiguration)) {
            log.error("javax.security.auth.login.Configuration is not DynamicJaasConfiguration.");
            throw new IOException(
                    "javax.security.auth.login.Configuration is not DynamicJaasConfiguration.");
        }

        AppConfigurationEntry[] entrys = conf.getAppConfigurationEntry(loginContextName);
    }

    private static UserGroupInformation loginHadoopPass(String principal, String password)
            throws IOException {
        try {
            LoginContext loginContext = kinit(principal, password);
            UserGroupInformation.loginUserFromSubject(loginContext.getSubject());
            return UserGroupInformation.getCurrentUser();
        } catch (IOException e) {
            log.error("login failed with " + principal + ".");
            log.error("perhaps cause 1 is " + "LOGIN_FAILED_CAUSE_PASSWORD_WRONG" + ".");
            log.error("perhaps cause 2 is " + "LOGIN_FAILED_CAUSE_TIME_WRONG" + ".");
            log.error("perhaps cause 3 is " + "LOGIN_FAILED_CAUSE_AES256_WRONG" + ".");
            log.error("perhaps cause 4 is " + "LOGIN_FAILED_CAUSE_PRINCIPAL_WRONG" + ".");
            log.error("perhaps cause 5 is " + "LOGIN_FAILED_CAUSE_TIME_OUT" + ".");

            throw e;
        } catch (LoginException e) {
            throw new IOException(e.getMessage());
        }
    }

    public static synchronized UserGroupInformation loginPassword(
            String userPrincipal, String password) throws IOException {

        setPassJaasConf(DEFAULT_LOGIN_CONTEXT_NAME, userPrincipal, password);
        // 4.login and check for hadoop
        UserGroupInformation ugi = loginHadoopPass(userPrincipal, password);
        log.info("Login success!!!!!!!!!!!!!!");
        return ugi;
    }
    /** 刷新krb内容信息 */
    public static void refreshConfig() {
        try {
            sun.security.krb5.Config.refresh();
            Field defaultRealmField = KerberosName.class.getDeclaredField("defaultRealm");
            defaultRealmField.setAccessible(true);
            defaultRealmField.set(
                    null,
                    org.apache.hadoop.security.authentication.util.KerberosUtil.getDefaultRealm());
            // reload java.security.auth.login.config
            javax.security.auth.login.Configuration.setConfiguration(null);
        } catch (Exception e) {
            log.warn(
                    "resetting default realm failed, current default realm will still be used.", e);
        }
    }

    public static String getPath() {
        String path =
                KerberosUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        if (System.getProperty("os.name").contains("dows")) {
            path = path.substring(1, path.length());
        }
        if (path.contains("jar")) {
            path = path.substring(0, path.lastIndexOf("."));
            return path.substring(0, path.lastIndexOf("/"));
        }
        return path.replace("target/classes/", "");
    }

    private static LoginContext kinit(final String userName, final String password)
            throws LoginException {
        LoginContext loginContext =
                new LoginContext(
                        DEFAULT_LOGIN_CONTEXT_NAME,
                        new CallbackHandler() {
                            @Override
                            public void handle(Callback[] callbacks)
                                    throws IOException, UnsupportedCallbackException {
                                for (Callback c : callbacks) {
                                    if (c instanceof NameCallback) {
                                        ((NameCallback) c).setName(userName);
                                    }
                                    if (c instanceof PasswordCallback) {
                                        ((PasswordCallback) c).setPassword(password.toCharArray());
                                    }
                                }
                            }
                        });
        loginContext.login();
        return loginContext;
    }

    private static class DynamicJaasConfiguration extends javax.security.auth.login.Configuration {

        private static final Map<String, String> BASIC_JAAS_OPTIONS = new HashMap<String, String>();

        static {
            String jaasEnvVar = System.getenv("HBASE_JAAS_DEBUG");
            if (jaasEnvVar != null && "true".equalsIgnoreCase(jaasEnvVar)) {
                BASIC_JAAS_OPTIONS.put("debug", "true");
            }
        }

        private static final Map<String, String> PASSWORD_KERBEROS_OPTIONS =
                new HashMap<String, String>();

        static {
            PASSWORD_KERBEROS_OPTIONS.put("useKeyTab", "false");
            PASSWORD_KERBEROS_OPTIONS.put("storeKey", "false");
            PASSWORD_KERBEROS_OPTIONS.put("doNotPrompt", "false");
            PASSWORD_KERBEROS_OPTIONS.put("refreshKrb5Config", "false");
            PASSWORD_KERBEROS_OPTIONS.put("clearPass", "true");
            PASSWORD_KERBEROS_OPTIONS.put("useTicketCache", "false");
        }

        private static final AppConfigurationEntry PASSWORD_KERBEROS_LOGIN =
                new AppConfigurationEntry(
                        org.apache.hadoop.security.authentication.util.KerberosUtil
                                .getKrb5LoginModuleName(),
                        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
                        PASSWORD_KERBEROS_OPTIONS);

        private static final AppConfigurationEntry[] KEYTAB_KERBEROS_CONF =
                new AppConfigurationEntry[] {PASSWORD_KERBEROS_LOGIN};

        private javax.security.auth.login.Configuration baseConfig;

        private final String loginContextName;

        private final boolean useTicketCache;

        private final String password;

        private final String principal;

        public DynamicJaasConfiguration(
                String loginContextName, String principal, String keytabFile) throws IOException {
            this(
                    loginContextName,
                    principal,
                    keytabFile,
                    keytabFile == null || keytabFile.length() == 0);
        }

        private DynamicJaasConfiguration(
                String loginContextName, String principal, String password, boolean useTicketCache)
                throws IOException {
            try {
                this.baseConfig = javax.security.auth.login.Configuration.getConfiguration();
            } catch (SecurityException e) {
                this.baseConfig = null;
            }
            this.loginContextName = loginContextName;
            this.useTicketCache = useTicketCache;
            this.password = password;
            this.principal = principal;

            initKerberosOption();
            log.info(
                    "JaasConfiguration loginContextName="
                            + loginContextName
                            + " principal="
                            + principal
                            + " useTicketCache="
                            + useTicketCache
                            + " password="
                            + password);
        }

        private void initKerberosOption() throws IOException {
            if (!useTicketCache) {
                // PASSWORD_KERBEROS_OPTIONS.put("keyTab", "false");
                PASSWORD_KERBEROS_OPTIONS.put("useKeyTab", "false");
                PASSWORD_KERBEROS_OPTIONS.put("useTicketCache", useTicketCache ? "true" : "false");
            }
            PASSWORD_KERBEROS_OPTIONS.put("principal", principal);
        }

        @Override
        public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {
            if (loginContextName.equals(appName)) {
                return KEYTAB_KERBEROS_CONF;
            }
            if (baseConfig != null) {
                return baseConfig.getAppConfigurationEntry(appName);
            }
            return (null);
        }
    }
}
